The development of software applications typically involves writing software code in a high-level programming language and translating the code into a lower-level machine language that can be executed by a computer system. The programming language itself is generally a set of specifications, such as definitions of acceptable syntaxes, instruction sets, and other features. Exploiting these specifications in a computational environment typically involves implementing additional functionality, such as corresponding compiler and runtime functionality. For example, compiler functions can translate code from high-level “source code” (e.g., human-readable instructions written according to the programming language specifications) into a lower-level “executable code” (e.g., corresponding machine-readable instructions). Once compiled, runtime functions can use runtime libraries, memory locations, etc. to schedule and carry out instructions according to the executable code. Accordingly, implementation of new programming language specifications typically involves development of new compiler and/or runtime functionality for handling such specifications.
New compiler and/or runtime functionality can also be developed to exploit new features of computational systems. Many modern compiler and runtime functions seek to exploit multi-processing execution environments (e.g., computational environments having multi-core, multi-thread processors), for example, by parallelizing computational operations and/or performing other scheduling optimizations. Some programming languages use various groupings of operations (e.g., loops) that can be identified and analyzed by certain compilers to find multi-processing candidates. Other programming languages allow programmers to explicitly define groups of operations into so-called “tasks” that can effectively self-identify to a compiler and/or a runtime function as multi-processing candidates.
Recently, new programming language specifications have begun to support “task dependence,” by which conditions of one task are intended by the programmer to depend on conditions of another task. For example, if a programmer intends for Task A to write a value to a memory location and for Task B subsequently to read that value from that location, Task B can be said to depend on Task A (i.e., it is undesirable for Task B to perform its read operation before Task A finishes its write operation). While syntaxes, instructions, and other features relating to task dependence are specified by the programming language, ensuring that code is executed in a manner that preserves task dependencies at runtime can involve overcoming a number of hurdles.